S04-01 Web-DOM
[TOC]
概述
API
API(Application Programming Interface,应用程序编程接口):是一组预定义的函数、协议和工具,用于构建/集成应用程序,隐藏内部实现细节,仅暴露必要交互接口。
- 任何开发语言都有自己的API
- API的特征输入和输出(I/O)
- var max = Math.max(1, 2, 3);
- API的使用方法(console.log('adf'))
Web API
Web API:是浏览器提供的一组预定义接口,允许 JS 代码与浏览器功能、硬件设备或系统服务交互,从而赋予网页动态操作能力和高级功能。它是 Web 开发的核心基础,使开发者能够突破静态页面的限制,实现复杂的交互和数据通信。
常见API分类:
文档对象模型:操作 HTML/CSS 结构
- DOM API
网络通信:与服务器交换数据
- Fetch API
- XMLHttpRequest
客户端存储:在浏览器中持久化数据
- Web Storage
- IndexedDB
多媒体:图形绘制、音频处理
- Canvas
- Web Audio API
设备交互:访问硬件传感器
- Geolocation
- Device Orientation
异步任务管理:处理耗时操作
- Promise
- Web Workers
JS的组成
JS组成:JavaScript由以下三个部分组成:
- ECMAScript:JavaScript 的语法和基础功能标准,由 ECMA International 组织维护(最新版本为 ES2025)。
- DOM:将 HTML/XML 文档结构化为树状对象模型,提供操作页面内容的 API。
- BOM:浏览器提供的与窗口、导航、屏幕等交互的非标准化 API。
DOM
概念
DOM(Document Object Model,文档对象模型,文档树模型):是浏览器将 HTML/XML 文档解析为的结构化对象模型,它允许JS程序动态访问和操作页面内容、结构与样式。
DOM本质:
- 内存表示:HTML 文档被浏览器解析为树状数据结构,每个标签、属性、文本都成为树中的节点(Node)。
- 编程接口:提供 JavaScript 可调用的 API,实现页面交互(如点击事件、内容更新)。
- 动态关联:DOM 与渲染引擎实时同步,修改 DOM 会触发浏览器重绘/重排。
DOM Tree
DOM Tree:一个页面不只是有html、head、body元素,也包括很多子元素,这些元素最终会在HTML结构中形成一个树结构。在抽象成DOM对象的时候,它们也会形成一个树结构,该结构就叫DOM Tree。
DOM关系图
DOM相当于是JS和HTML、CSS之间的桥梁,通过浏览器提供给我们的DOM API,我们可以对元素以及其中的内容做任何事。
BOM
BOM(Browser Object Model,浏览器对象模型):是浏览器提供的与当前窗口或标签页交互的对象集合,允许开发者控制浏览器行为(如导航、窗口尺寸、历史记录等)。它与 DOM(文档对象模型)相辅相成,共同构成 Web 前端开发的核心能力。
对比DOM:
维度 | BOM | DOM |
---|---|---|
作用对象 | 浏览器窗口、导航、历史等 | 网页文档内容(HTML/XML) |
标准化 | 无统一标准(但现代浏览器行为趋同) | W3C 标准规范 |
核心对象 | window 、location 、history | document 、元素节点 |
应用场景 | 控制浏览器行为 | 操作页面内容 |
核心对象:
- window:顶级对象,所有全局变量和函数都是 window 的属性。
- navigator:浏览器信息。
- location:URL 管理。
- history:浏览历史。
- screen:屏幕信息。
document
概述
document:Document
,是浏览器环境中全局对象 window 的属性,代表当前加载的网页文档,是 DOM 的入口点。它提供了访问和操作页面内容的核心 API,是前端开发最基础且重要的对象。
核心作用:
- DOM访问入口:整个 HTML 文档的根节点。
- 页面元数据管理:标题、URL、Cookie 等。
- 动态内容操作:增删改查元素、样式、事件。
- 资源加载控制:脚本、样式表、图片的加载。
API-Document
属性:
- document.documentElement:
Element
,只读,返回当前文档的直接子节点。对于 HTML 文档一般代表该文档的 html 元素。 - document.body:
body|frameset|null
,获取或设置当前文档的 body 或 frameset节点(不常用设置)。 - document.head:
HTMLHeadElement
,只读,返回当前文档的 head 元素。 - document.title:
string
,获取或设置当前文档的标题。 - document.doctype:
DocumentType
,只读,返回当前文档的文档类型定义DTD(<!DOCTYPE html>
)。 - document.images:
HTMLCollection
,只读,返回当前文档中所包含的图片的 HTMLCollection。 - document.scripts:
HTMLCollection
,只读,返回包含文档中所有的 script 元素的 HTMLCollection。 - document.links:
HTMLCollection
,只读,返回一个包含文档中所有超链接的 HTMLCollection。 - document.forms:
HTMLCollection
,只读,返回一个包含当前文档中所有表单元素 form 的 HTMLCollection。 - document.cookie:
string
,返回一个使用分号分隔的 cookie 列表,或设置(写入)一个 cookie。
方法:
元素查询:
- 现代方法:
- document.querySelector() / el.querySelector():
(selectors)
,返回文档中与指定的选择器匹配的第一个元素节点。 - document.querySelectorAll() / el.querySelectorAll():
(selectors)
,返回包含文档中与指定的选择器匹配的所有元素节点的列表。 - 传统方法:
- document.getElementById():
(id)
,返回与ID匹配的元素。 - document.getElementsByClassName() / el.getElementsByClassName():
(classNames)
,返回匹配给定类名的元素列表。 - document.getElementsByTagName() / el.getElementsByTagName():
(tagname|*)
,返回匹配给定标签名的元素列表。
元素创建:
- document.createElement():
(tagName,options?)
,用给定标签名创建一个新的元素,支持自定义标签。
文本写入:
- document.write():
(...content)
,不建议,用于向 HTML 文档直接写入内容的方法(可能引发副作用)。
DOM操作
导航
节点导航
节点导航:如果我们获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的导航。
常见节点导航 API:
适用于处理 所有类型节点(元素、文本、注释等),返回的节点可能包含非元素类型。
- node.parentNode:
Node|null
,只读,返回一个当前节点的父节点。 - node.previousSibling:
Node|null
,只读,返回与该节点同级的前一个节点。 - node.nextSibling:
Node|null
,只读,返回与该节点同级的下一个节点。 - node.childNodes:
NodeList
,只读,返回一个包含了该节点所有子节点的动态变化的的NodeList。 - node.firstChild:
Node|null
,只读,返回该节点的第一个子节点。 - node.lastChild:
Node|null
,只读,返回该节点的最后一个子节点。
元素导航@
常见元素导航 API:
专用于 元素节点(Element),自动过滤非元素节点,更符合日常开发需求。
- node.parentElement:
Node|null
,只读,返回一个当前节点的父元素节点。 - node.previousElementSibling:
Node|null
,只读,返回与该节点前一个兄弟元素。 - node.nextElementSibling:
Node|null
,只读,返回与该节点下一个兄弟元素。 - node.children:
HTMLCollection
,只读,返回一个包含了该节点所有子元素的动态集合。 - node.firstElementChild:
Node|null
,只读,返回该节点的第一个子元素。 - node.lastElementChild:
Node|null
,只读,返回该节点的最后一个子元素。
table导航
table:在 DOM 中拥有独特的导航方法,通过特定属性和集合可快速定位行列数据。
常见table导航 API:
- table
- table.caption:
HTMLTableCaptionElement|null
,获取/设置表格标题元素(如果存在)。 - table.tHead:
HTMLTableSectionElement|null
,获取/设置表格的 thead 元素(如果存在)。 - table.tFoot:
HTMLTableSectionElement|null
,获取/设置表格的 tfoot 元素(如果存在)。 - table.tBodies:
HTMLCollection
,只读,返回包含元素中的所有 tbody 的 动态集合。 - table.rows:
HTMLCollection
,只读,返回包含元素中的所有 tr 的 动态集合。 - row
- row.cells:
HTMLCollection
,只读,获取行内所有单元格(td,th)的动态集合。 - row.sectionRowIndex:
number
,只读,获取行在表格body区域中的索引。 - row.rowIndex:
number
,只读,获取行在整个表格中的索引。 - cell
- cell.cellIndex:
number
,只读,获取单元格在行中的索引。 - cell.colSpan:
number
,获取/设置跨列数。 - cell.rowSpan:
number
,获取/设置跨行数。
练习:实现如下效果【
form导航
form:是用户输入的核心组件,DOM 提供了丰富的 API 用于访问和操作表单及其控件。
常见form导航 API:
- document
- document.forms:
HTMLCollection
,只读,返回一个包含当前文档中所有表单元素 form 的 HTMLCollection。 - form
- form.elements:
HTMLFormControlsCollection
,只读,返回 form 元素中包含的所有表单控件。
访问表单元素控件的方法:
通过索引访问
jsconst firstElement = form.elements[0]; // 第一个表单控件
通过 name 或 id 访问
html<form id="myForm"> <input type="text" name="username" id="user"> <input type="radio" name="gender" value="male"> <input type="radio" name="gender" value="female"> </form>
jsconst form = document.getElementById('myForm'); // 访问单个元素 const usernameInput = form.elements.username; // 或 form.elements['username'] // 访问同名元素(如单选按钮) const genderRadios = form.elements.gender; // 返回 RadioNodeList console.log(genderRadios.value); // 输出选中的值(如 "male")
使用 namedItem() 方法
jsconst userElement = form.elements.namedItem('user'); // 通过 id="user" 查找
获取元素
我们想要操作页面上的某部分(显示/隐藏,动画),需要先获取到该部分对应的元素,才能进行后续操作。
传统方法
传统方法:
- document.getElementById():
(id)
,返回与ID匹配的元素。 - document.getElementsByClassName() / el.getElementsByClassName():
(classNames)
,返回匹配给定类名的元素列表。 - document.getElementsByTagName() / el.getElementsByTagName():
(tagname|*)
,返回匹配给定标签名的元素列表。
示例:
const header = document.getElementById('main-header');
const buttons = document.getElementsByClassName('btn primary');
const images = document.getElementsByTagName('img');
现代方法
现代方法:
- document.querySelector() / el.querySelector():
(selectors)
,返回文档中与指定的选择器匹配的第一个元素节点。 - document.querySelectorAll() / el.querySelectorAll():
(selectors)
,返回包含文档中与指定的选择器匹配的所有元素节点的列表。
节点属性
节点常见属性:
- node.nodeType:
number
,只读,用于标识节点的类型。 - node.nodeName:
DOMString
,只读,返回当前节点的节点名称。 - el.tagName:
string
,只读,返回当前元素的标签名。 - el.innerHTML:
DOMString
,获取/设置 HTML 语法表示的元素的后代。 - node.textContent:
string|null
,返回/设置一个元素内所有子节点及其后代的文本内容。 - node.outerHTML:
DOMString
,获取/设置 HTML 语法表示的元素以及其后代。 - node.data/nodeValue:``,获取非元素节点的文本内容。
nodeType
node.nodeType:number
,只读,用于标识节点的类型。
DOM常见节点类型:
类型(Node Type) | 常量 | 常量值 | 示例 |
---|---|---|---|
元素节点 | Node.ELEMENT_NODE | 1 | <div> , <p> , <a> |
属性节点(已废弃) | Node.ATTRIBUTE_NODE | 2 | class="content" |
文本节点 | Node.TEXT_NODE | 3 | '段落' |
注释节点 | Node.COMMENT_NODE | 8 | <!-- 注释 --> |
文档节点 | Node.DOCUMENT_NODE | 9 | document 对象 |
文档类型节点 | Node.DOCUMENT_TYPE_NODE | 10 | <!DOCTYPE html> |
示例:
判断元素节点
jsconst element = document.getElementById("header"); if (element.nodeType === Node.ELEMENT_NODE) { // 或 element.nodeType === 1 console.log("这是一个元素节点"); }
过滤文本节点
html<div id="content">Hello <!-- 注释 --> World</div>
jsconst div = document.getElementById("content"); div.childNodes.forEach(node => { if (node.nodeType === Node.TEXT_NODE) { console.log("文本内容:", node.textContent); // 输出 "Hello " 和 " World" } });
元素操作
创建元素
- document.write():
(...content)
,不建议,用于向 HTML 文档直接写入内容的方法(可能引发副作用)。 - el.innerHTML:
string
,读取或修改 HTML 元素内容的属性。可以操作元素内部的 HTML 结构(包括标签和文本)。 - document.createElement():
(tagName,options?)
,用给定标签名创建一个新的元素,支持自定义标签。
插入元素@
- el.append():
(...nodesOrStrings)
,用于向 DOM 节点的子节点列表末尾插入一个或多个节点或字符串的方法。 - el.prepend():
(...nodesOrStrings)
,用于向 DOM 节点的子节点列表开头插入一个或多个节点或字符串的方法。 - el.before():
(...nodesOrStrings)
,用于在指定节点的前面插入一个或多个节点或字符串的方法。 - el.after():
(...nodesOrStrings)
,用于在指定节点的后面插入一个或多个节点或字符串的方法。 - el.replaceWith():
(...nodesOrStrings)
,用于替换 DOM 节点的方法,它可以将当前节点替换为一个或多个新节点或字符串。
公共特性:
- 不会解析HTML标签:字符串参数会被隐式转换为 TextNode。
- 移动已有节点:如果传入的节点已存在于文档中,会先被移除原位置再插入到新位置。
- 性能优化:操作多个节点时,优先使用
DocumentFragment
减少重排次数。
示例:
基础用法
jsconst list = document.createElement("ul"); // append() const lastItem = document.createElement('li') lastItem.textContent = 'lastItem' list.append(lastItem) // prepend() const lastItem = document.createElement('li') firstItem.textContent = 'firstItem' list.append(firstItem) // before() const prevSibling = document.createElement('div') prevSibling.textContent = 'prevSibling' list.before(prevSibling) // after() const nextSibling = document.createElement('p') nextSibling.textContent = 'nextSibling' list.after(nextSibling) // replaceWith() const olEl = document.createElement('ol') list.replaceWith(olEl)
移除元素
el.remove():()
,用于从 DOM 树中移除当前元素节点的方法。直接作用于目标元素,无需通过父节点操作。
克隆元素
node.cloneNode():(deep?)
,用于克隆 DOM 节点的方法,可生成当前节点的副本。通过参数控制是否深度复制子节点,适用于需要复用或动态生成相似结构的场景。
传统元素操作方法
在很多地方我们也会看到一些旧的操作方法:
- parentNode.appendChild():
(childNode)
,用于将一个节点添加到指定父节点的子节点列表末尾的方法。 - parentNode.insertBefore():
(newNode, referenceNode)
,用于在父节点的指定子节点前插入一个新节点的方法。 - parentNode.replaceChild():
(newNode, oldNode)
,用于替换父节点中的指定子节点的方法。 - parentNode.removeChild():
(childNode)
,用于从父节点中移除指定子节点的方法。
公共特性:
- 移动已有节点:如果传入的节点已存在于文档中,会先被移除原位置再插入到新位置。
- 性能优化:操作多个节点时,优先使用
DocumentFragment
减少重排次数。 - 仅支持单个节点:每次只能添加一个节点,若需插入多个节点,需多次调用或使用 DocumentFragment。
- 参数必须为节点对象:若传入非节点对象(如字符串、数字),会抛出 TypeError。
- 必须是直接子节点:参数中的节点必须是parentNode的直接子节点,否则会抛出 DOMException。
- 旧节点的处理:被替换/移除的 oldNode/childNode 仍然存在于内存中,可重新插入到 DOM 或其他位置。
- 事件监听器处理:被替换/移除的 oldNode/childNode 的事件监听器不会自动移除,需手动解绑。
示例:
基础用法
jsconst parent = document.createElement("div"); // appendChild() const lastChild = document.createElement("p"); lastChild.textContent = "lastChild"; parent.appendChild(lastChild); // insertBefore() const insertChild = document.createElement("span"); insertChild.textContent = "insertChild"; parent.insertChild(insertChild, lastChild); // replaceChild() const replaceChild = document.createElement("strong"); replaceChild.textContent = "replaceChild"; parent.replaceChild(replaceChild, insertChild); // removeChild() parent.removeChild(lastChild);
案例
动态创建列表
时间显示
倒计时
尺寸位置滚动
元素尺寸位置滚动
在 JS 中,获取和操作元素的尺寸、位置及滚动状态是前端开发中常见的需求。
元素尺寸:
属性 | 计算方法 |
---|---|
clientWidth / clientHeight | 内容 + padding (不包含滚动条) |
offsetWidth / offsetHeight | 内容 + padding + border (包含滚动条) |
scrollWidth / scrollHeight | 可见内容 + 滚动出去的内容 |
元素位置:
属性 | 计算方法 |
---|---|
offsetLeft / offsetTop | 与当前元素最近的定位父级元素之间的距离 |
scrollLeft / scrollTop | 盒子内容滚动出去的距离(包括边框) |
其他:
属性 | 计算方法 |
---|---|
clientLeft / clientTop | 左/上边框的宽度 |
window尺寸滚动
在 JS 中,window
对象提供了与**浏览器窗口(视口)**尺寸及滚动状态相关的属性和方法。
window尺寸:
属性 | 计算方法 |
---|---|
window.innerWidth / innerHeight | 布局视口宽度/高度(包含垂直滚动条的宽度) |
window.outerWidth / outerHeight | 浏览器窗口外部的宽度/高度。 |
document.documentElement.clientWidth / clientHeight | HTML元素的client宽度/高度 |
document.documentElement.scrollWidth / scrollHeight | HTML元素的scroll宽度/高度 |
window滚动:
属性 | 计算方法 |
---|---|
window.scrollX / scrollY | 文档水平/垂直滚动的像素数 |
window.pageXOffset / pageYOffset | 等同于 window.scrollX / scrollY |
滚动方法:
- el.scrollTo() / window.scrollTo():
(x, y)
或({left,top,behavior})
,用于控制可滚动元素滚动位置的方法。适用于overflow设置为 scroll 或 auto 的元素。 - el.scrollBy() / window.scrollBy():
(x, y)
或({left,top,behavior})
,用于控制可滚动元素相对当前位置滚动的方法。 - el.scrollIntoView():
(alignToTop)
或({behavior,block,inline})
,用于将元素滚动到浏览器窗口或可滚动容器可视区域的方法。
属性
元素属性 Attribute
元素属性(Attribute):是添加到 HTML 标签中的额外信息,用于定义元素的行为、样式、数据或其他特性。属性以键值对的形式存在(如 name="value"
),为元素提供丰富的功能扩展。
语法格式:
<标签名 属性1="值1" 属性2="值2">内容</标签名>
<!-- 示例 -->
<a href="https://example.com" target="_blank">访问示例</a>
特性:
- 大小写不敏感
- 属性值为字符串类型
元素属性的分类:
标准Attribute:
- 全局属性
- 功能属性【
- 事件处理器属性
非标准Attribute:
全局属性
全局属性:是所有 HTML 元素共有的属性;它们可以用于所有元素,即使属性可能对某些元素不起作用。
常见全局属性:
- id:唯一标识符,用于CSS或JavaScript精准选择元素。
- class:为元素指定一个或多个类名(用空格分隔),用于CSS样式或JavaScript选择。
- title:
JS:el.title
,提供额外信息,通常显示为工具提示。 - style:
JS:el.style
,内联CSS样式(优先级高)。 - data-*:
JS:el.dataset
,存储自定义数据(*
为自定义名称),通过JavaScript的dataset
访问。 - hidden:
JS:el.hidden
,隐藏元素(等效于display: none
)。 - draggable:
JS:el.draggable
,控制元素是否可拖动(需配合拖放API)。
表单元素属性【
- value 用于大部分表单元素的内容获取(option除外)
- type 可以获取input标签的类型(输入框或复选框等)
- disabled 禁用属性
- checked 复选框选中属性
- selected 下拉菜单选中属性
元素对象属性 Property
通用属性操作
通用属性方法:
通用属性方法支持所有的attribute的访问,包括标准和非标准的属性:
- el.attributes:
NamedNodeMap
,只读,返回一个 NamedNodeMap 对象,其中包含相应 HTML 元素的指定属性。 - el.getAttribute():
(attributeName)
,从当前节点获取指定属性的值,并以字符串形式返回。 - el.setAttribute():
(name, value)
,设置当前节点的指定属性值,若属性不存在则创建。 - el.hasAttribute():
(attributeName)
,返回一个布尔值,表示元素是否具有指定属性。 - el.removeAttribute():
(attrName)
,从当前节点删除指定属性。
示例:
获取所有属性
html<a id="link" href="https://example.com" target="_blank">链接</a>
jsconst link = document.getElementById('link'); const attrs = link.attributes; // 输出所有属性及值 Array.from(attrs).forEach(attr => { console.log(`${attr.name}: ${attr.value}`); }); // 输出: // id: link // href: https://example.com // target: _blank
访问指定属性
jsconst link = document.querySelector('a'); const href = link.getAttribute('href'); // 返回链接地址(字符串)
设置指定属性
jsconst img = document.querySelector('img'); img.setAttribute('alt', '产品图片'); // 设置 alt 属性
检查属性存在
jsconst checkbox = document.querySelector('input[type="checkbox"]'); if (checkbox.hasAttribute('checked')) { console.log('复选框默认选中'); }
移除指定属性
jsconst input = document.querySelector('input'); input.removeAttribute('disabled'); // 移除禁用状态
标准属性操作
对于标准attribute,会在DOM对象上创建与其对应的property属性:
Property对比Attribute:
- 大多数情况下,它们是相互作用的:
- 改变property,通过attribute获取的值会随之变化
- 改变attribute,property对应的值也会随之变化
- 除非特殊情况,设置/获取属性推荐使用property的方式,因为它默认是有类型提示的。
类名 className/classList
操作class的方法:
方法一:通过 className 操作
缺点:会覆盖之前的class
示例:
var box = document.getElementById('box');
box.className = 'show';
方法二:通过 classList 操作(推荐)
如果需要添加或移除单个class,可以使用classList属性。
- el.classList:
DOMTokenList
,只读,返回元素 class 属性可迭代的动态集合。可用于操作 class 集合。 - el.classList.add():
(className)
,添加一个类。 - el.classList.remove():
(className)
,移除一个类。 - el.classList.toggle():
(className)
,如果类不存在就添加类,存在就移除该类。 - el.classList.contains():
(className)
,检查是否存在指定类。 - el.classList.replace():
(oldClass, newClass)
,将旧类名替换成新类名。
示例:
基本使用
html<div class="foo"></div>
jsconst div = document.createElement("div"); // 1. 移除 div.classList.remove("foo"); // <div class=""></div> // 2. 添加 div.classList.add("anotherclass"); // <div class="anotherclass"></div> // 3. 切换 div.classList.toggle("visible"); // <div class="anotherclass visible"></div> // 4. 是否存在 console.log(div.classList.contains("foo")); // false
高级使用
js// 1. 添加或移除多个类值 div.classList.add("foo", "bar", "baz"); div.classList.remove("foo", "bar", "baz"); // 2. 使用展开语法添加或移除多个类值 const cls = ["foo", "bar"]; div.classList.add(...cls); div.classList.remove(...cls); // 3. 将类值 "foo" 替换成 "bar" div.classList.replace("foo", "bar");
样式 style
基本使用
htmlElement.style:CSSStyleDeclaration
,:获取/设置元素的样式属性。可直接操作元素的内联样式。
语法格式:
// 1. 获取样式值
const value = element.style.propertyName;
// 2. 设置样式值
element.style.propertyName = "value";
// 3. 批量设置样式(覆盖原有内联样式)
element.style.cssText = "property1: value1; property2: value2;";
// 4. 使用 setProperty() 方法(支持连字符属性名)
element.style.setProperty("property-name", "value");
核心特性:
- 驼峰式命名转换:CSS 属性名在 JavaScript 中需转换为驼峰式命名。
- 仅操作内联样式:仅控制元素的内联样式(即
style
属性定义的样式),不影响通过 CSS 类或外部样式表定义的样式。 - 值必须为字符串:即使数值类型(如 opacity、zIndex),也需转换为字符串,需要带单位(如px)。
示例:
const box = document.getElementById("box");
// 1. 设置背景色和宽度
box.style.backgroundColor = "#f00";
box.style.width = "200px";
// 2. 使用 setProperty 设置边框
box.style.setProperty("border", "2px solid black");
// 3. 使用 cssText 批量设置样式
element.style.cssText = "color: red; margin: 10px;";
getComputedStyle()
通过style只能读取到内联样式,但是style和css文件中的样式是读取不到的。此时可以通过 getComputedStyle() 来读取。
window.getComputedStyle():(element,pseudoEl?)
,只读,用于获取元素最终计算后的 CSS 样式的方法。
设置多个样式@
方式一:style.cssText
,可以通过修改 style.cssText
属性来一次性设置多个 CSS 样式
// 获取元素
const element = document.getElementById("myElement");
// 一次性修改多个样式
element.style.cssText = "background-color: red; font-size: 20px; color: white;";
方式二:setAttribute()
,可以通过 setAttribute
方法设置 style
一次性修改多个样式
// 获取元素
const element = document.getElementById("myElement");
// 一次性修改多个样式
element.setAttribute("style", "background-color: red; font-size: 20px; color: white;");
方式三:Object.assign()
,可以通过JS的 Object.assign()
方法将多个样式合并到元素的 style
属性中。
// 获取元素
const element = document.getElementById("myElement");
// 使用 Object.assign 一次性设置多个样式
Object.assign(element.style, {
backgroundColor: 'red',
fontSize: '20px',
color: 'white'
});
自定义属性 dataset
el.dataset:DOMStringMap
,用于访问和操作 HTML 元素的自定义数据属性(data-*
) 的属性。
语法格式:
// 获取元素的 dataset 对象
const data = element.dataset;
// 读取属性值
const value = element.dataset.keyName;
// 设置属性值
element.dataset.keyName = "newValue";
// 删除属性
delete element.dataset.keyName;
核心特性:
- 属性名转换规则:HTML 中的
data-*
属性名会自动转换为驼峰式命名(如data-user-id
→userId
)。 - 值的隐式转换:所有值均为字符串,需手动转换其他类型。设置非字符串值时会自动转为字符串。
示例:
存储和读取复杂数据(JSON)
html<div data-config='{"theme": "dark", "lang": "en"}'></div> <script> const div = document.querySelector("div"); const config = JSON.parse(div.dataset.config); console.log(config.theme); // "dark" </script>
动态更新 UI 状态
html<button data-click-count="0" onclick="incrementCount(this)">点击次数:0</button> <script> function incrementCount(button) { const count = parseInt(button.dataset.clickCount) + 1; button.dataset.clickCount = count.toString(); button.textContent = `点击次数:${count}`; } </script>
事件
概念
认识事件
事件(Event):是用户或浏览器自身执行的某种动作的抽象表示。例如:点击按钮、滚动页面、按下键盘、加载完成一个图片等。事件是 JS 实现交互性的核心机制,允许代码对用户或系统的行为作出响应。
事件的核心概念:
- 事件类型(Event Type)
- 事件目标(Event Target)
- 事件处理函数(Event Handler)
- 事件对象(Event Object)
事件处理方式
常见的事件处理方式有以下3种:
方式一:HTML属性,直接在 HTML 元素标签中通过 on[event]
属性绑定事件处理函数。
<button onclick="alert('Clicked!')">点击</button>
特点:
- 简单直接:适合快速测试。
- 代码耦合:HTML 与 JavaScript 混杂,难以维护。
- 全局污染:依赖全局函数,易引发命名冲突。
方式二:DOM属性,通过 JavaScript 为 DOM 元素的 on[event]
属性赋值。
const button = document.querySelector('button');
button.onclick = function() {
console.log('Clicked!');
};
特点:
- 简单易用:快速绑定单个事件。
- 单监听器限制:同一事件只能绑定一个处理函数(后绑定的会覆盖之前的)。
- 兼容性好:所有浏览器支持。
方式三:addEventListener(推荐),使用 addEventListener
方法绑定事件监听器,支持多个监听器和更精细的控制。
const button = document.querySelector("button");
function handleClick(event) {
console.log("点击事件触发", event.target);
}
// 绑定事件
button.addEventListener("click", handleClick);
// 移除事件
button.removeEventListener("click", handleClick);
特点:
- 多监听器支持:可绑定多个处理函数,按注册顺序执行。
- 灵活控制阶段:通过
capture
参数指定事件阶段(捕获或冒泡)。 - 更安全的移除:需通过
removeEventListener
移除,需保持函数引用一致。
事件流
事件流(Event Flow):定义了事件如何在 DOM 树中传递,以及何时触发绑定在元素上的事件处理函数。
事件流的三个阶段:事件流分为三个阶段,按顺序依次执行:
- 捕获阶段(Capture Phase)
- 事件从
window
对象开始,逐级向下传播到目标元素的父级,直到到达目标元素本身。 - 目的:允许在事件到达目标前拦截和处理事件。
- 默认不监听:除非显式指定,否则大多数事件监听器不会在此阶段触发。
- 事件从
- 目标阶段(Target Phase)
- 事件到达目标元素(触发事件的元素)。
- 如果事件监听器绑定在目标元素上,无论它是监听捕获还是冒泡阶段,都会在此阶段触发。
- 冒泡阶段(Bubble Phase)
- 事件从目标元素开始,逐级向上冒泡回
window
对象。 - 默认监听阶段:大多数事件(如
click
)默认在冒泡阶段触发监听器。
- 事件从目标元素开始,逐级向上冒泡回
指定事件流监听阶段:通过 addEventListener 的第三个参数(useCapture)控制事件触发阶段。
true
:在捕获阶段触发监听器。false
(默认值):在冒泡阶段触发监听器。
// 捕获阶段触发
document.getElementById("parent").addEventListener("click", () => {
console.log("父元素 - 捕获阶段");
}, true);
// 冒泡阶段触发
document.getElementById("child").addEventListener("click", () => {
console.log("子元素 - 冒泡阶段");
}, false);
事件流的兼容性与历史:
- 早期浏览器差异:
- IE8 及更早版本仅支持冒泡阶段。
- Netscape 支持捕获阶段,但未被广泛采用。
- 标准化:W3C 统一了事件流模型,规定先捕获后冒泡。
事件对象 event
事件对象(Event Object):是事件触发时自动创建的对象,包含与事件相关的详细信息和方法。通过事件处理函数的参数可以访问该对象,用于控制事件行为和获取上下文数据。
当一个事件发生时,就会有和这个事件相关的很多信息,如事件类型、点击的元素、点击的位置等。这些信息会被封装到一个由浏览创建的event对象中。
访问方式:event对象会在传入的事件处理函数回调时被系统传入,我们可以在回调函数中访问该event对象
常见属性方法【
属性:
- 事件目标
- event.target:
EventTarget
,触发事件的元素(如用户直接点击的子元素)。 - event.currentTarget:
EventTarget
,绑定事件处理函数的元素(等同于 this)。 - event.type:
string
,事件类型(如"click"
、"keydown"
)。 - event.eventPhase:
number
,事件流所处阶段。 - 尺寸(鼠标事件)
- mouseEvent.clientX / mouseEvent.clientY:
number
,只读,不带单位,鼠标指针相对于视口坐标系的 X/Y 坐标。 - mouseEvent.offsetX / mouseEvent.offsetY:
number
,只读,不带单位,鼠标指针相对于目标节点内边位置的 X/Y 坐标。 - mouseEvent.pageX / mouseEvent.pageY:
number
,只读,不带单位,鼠标指针相对于整个文档的 X/Y 坐标。 - mouseEvent.screenX / mouseEvent.screenY:
number
,只读,不带单位,鼠标指针相对于屏幕的 X/Y 坐标。 - 键盘事件
- keyboardEvent.key:
,
- keyboardEvent.code:
,
- keyboardEvent.keyCode:
,已废弃,
方法:
- event.preventDefault():
()
,用于阻止事件的默认行为(如表单提交、链接跳转)。 - event.stopPropagation():
()
,用于阻止事件在 DOM 树中继续传播(包括捕获和冒泡阶段)。 - event.stopImmediatePropagation():
()
,阻止事件继续传播,且停止执行当前元素的其他监听器。
事件目标
事件目标(Event Target):指代触发事件的 DOM 元素。通过事件对象 event 的 target
属性可访问它,用于精确识别事件源并实现灵活的交互逻辑。
event.target:EventTarget
,触发事件的元素(如用户直接点击的子元素)。
<div id="parent">
<button id="child">点击</button>
</div>
<script>
document.getElementById("parent").addEventListener("click", function(event) {
console.log(event.target); // 点击按钮时输出 <button id="child">
});
</script>
event.currentTarget:EventTarget
,绑定事件处理函数的元素(等同于 this)。
document.querySelector("#parent").addEventListener("click", function(event) {
console.log(event.currentTarget); // 输出 <div id="parent">
console.log(this === event.currentTarget); // true
});
示例:
事件委托
通过父元素监听子元素事件,利用 event.target 识别具体触发元素,优化性能
html<ul id="list"> <li>Item 1</li> <li>Item 2</li> </ul> <script> document.getElementById("list").addEventListener("click", function(event) { if (event.target.tagName === "LI") { console.log("点击了:", event.target.textContent); } }); </script>
动态交互
直接操作触发事件的元素(如修改样式、获取数据属性)
jsdocument.querySelector("button").addEventListener("click", function(event) { const target = event.target; target.style.backgroundColor = "red"; // 修改点击按钮的背景色 console.log(target.dataset.info); // 获取自定义属性 data-info 的值 });
阻止默认行为或传播
根据事件目标执行条件性控制
jsdocument.querySelector("a").addEventListener("click", function(event) { if (event.target.href.includes("blocked")) { event.preventDefault(); // 阻止特定链接跳转 } });
阻止事件默认行为
event.preventDefault():()
,用于阻止事件的默认行为(如表单提交、链接跳转)。
阻止事件传播
event.stopPropagation():()
,用于阻止事件在 DOM 树中继续传播(包括捕获和冒泡阶段)。
this
事件处理函数中的 this 指向根据绑定方式的不同而变化:
默认指向绑定事件的元素
当通过 addEventListener 或 DOM 属性(如 onclick) 绑定事件时,this 默认指向 触发事件的元素。
jsconst btn = document.getElementById("btn"); // DOM 属性绑定 btn.onclick = function() { console.log(this); // 输出:<button id="btn">点击</button> }; // addEventListener 绑定 btn.addEventListener("click", function() { console.log(this); // 输出:<button id="btn">点击</button> });
箭头函数中的 this 指向
箭头函数 没有自己的 this,它会继承外层作用域的 this。在事件处理中,可能导致 this 指向 window 或 undefined(严格模式)。
html<button id="btn">点击</button> <script> const btn = document.getElementById("btn"); btn.addEventListener("click", () => { console.log(this); // 输出:window(非严格模式)或 undefined(严格模式) }); </script>
事件委托中的 this 指向
在事件委托中,this 指向 绑定事件的父元素,而 event.currentTarget 与 this 相同。event.target 则是实际触发事件的子元素。
html<ul id="list"> <li>Item 1</li> <li>Item 2</li> </ul> <script> document.getElementById("list").addEventListener("click", function(event) { console.log(this); // 输出:<ul id="list">...</ul> console.log(event.currentTarget === this); // true console.log(event.target); // 输出:被点击的 <li> 元素 }); </script>
EventTarget
常见方法:
- eventTarget.addEventListener():
(type, listener, options? | useCapture?)
,用于为 DOM 元素绑定事件监听器的核心方法,支持精细控制事件触发的阶段、频率及生命周期。 - eventTarget.removeEventListener():
(type, listener, options? | useCapture?)
,用于移除通过 addEventListener 绑定的事件监听器的方法。正确使用它可以避免内存泄漏和重复事件触发问题。 - eventTarget.dispatchEvent():
(event)
,用于手动触发自定义或原生事件的方法。
事件委托
事件委托(Event Delegation):是一种利用事件冒泡机制,将子元素的事件监听统一委托给父元素处理,并通过 event.target 识别实际触发事件的子元素执行对应的逻辑,它是一种设计模式。
实现步骤:
绑定事件到父元素
jsparent.addEventListener("eventType", handler);
识别触发子元素
jsfunction handler(event) { const target = event.target; // 触发事件的子元素 }
筛选目标元素并处理
jsif (target.matches("selector")) { // 执行针对该子元素的逻辑 }
示例:
列表项点击事件
html<ul id="list"> <li>Item 1</li> <!-- 点击它 --> <li>Item 2</li> <li>Item 3</li> </ul> <script> document.getElementById("list").addEventListener("click", function(event) { const li = event.target.closest("li"); // 确保点击的是 li 或其子元素 if (li) { console.log("点击了:", li.textContent); } }); </script>
列表项排他点击事件
动态添加按钮处理
html<div id="container"> <!-- 动态添加的按钮 --> </div> <script> document.getElementById("container").addEventListener("click", function(event) { if (event.target.classList.contains("action-btn")) { console.log("按钮被点击:", event.target.dataset.action); } }); // 动态添加按钮 const newBtn = document.createElement("button"); newBtn.className = "action-btn"; newBtn.dataset.action = "delete"; newBtn.textContent = "删除"; document.getElementById("container").appendChild(newBtn); </script>
常见事件
常见事件:
鼠标事件:
- click:鼠标点击元素(按下并释放左键)。
- dblclick:鼠标双击元素。
- contextmenu:右键点击元素(弹出上下文菜单前)。
- mousedown / mouseup:鼠标按下 / 释放。
- mousemove:鼠标在元素内移动。
- mouseenter / mouseleave:不冒泡,鼠标进入 / 离开元素本身。仅在进入目标元素时触发一次,子元素之间移动不触发。
- mouseover / mouseout:冒泡,鼠标进入 / 离开元素或其子元素。鼠标在元素和子元素之间移动时会重复触发。
键盘事件:
- keydown:按下键盘任意键。
- keyup:松开键盘按键。
表单事件:
- submit:表单提交。
- reset:表单重置。
- input:输入框内容实时变化。
- change:表单元素值变化并失焦(如输入框、下拉框)。
- focus / blur:元素获得 / 失去焦点。
窗口事件:
- DOMContentLoaded:HTML 解析完成(无需等待样式、图片)。
- load:页面及资源(如图片)加载完成。
- beforeunload:窗口关闭或刷新前。
- resize:窗口大小变化。
- scroll:页面滚动。
过渡事件:
- transitionend:CSS 过渡结束播放。
触摸事件:
- touchstart:触摸开始。
- touchmove:触摸移动。
- touchend:触摸结束。
剪切板事件:
- copy / paste:复制 / 粘贴内容。
媒体事件:
- play / pause:媒体播放 / 暂停。